Skip to content

feat(usage): add subscription limit reporting for Claude, Codex, Grok, and OpenCode - #8515

Closed
AhmedShareef wants to merge 10 commits into
pingdotgg:mainfrom
AhmedShareef:feat/usage-limits
Closed

feat(usage): add subscription limit reporting for Claude, Codex, Grok, and OpenCode#8515
AhmedShareef wants to merge 10 commits into
pingdotgg:mainfrom
AhmedShareef:feat/usage-limits

Conversation

@AhmedShareef

@AhmedShareef AhmedShareef commented Aug 28, 2026

Copy link
Copy Markdown

The usage page showed transcript-based activity but nothing about how much of a subscription's rate limits were left, so users driving agents all day had no way to see a window running hot before hitting it.

This adds a Limits view to the usage page, backed by a new UsageLimitsService on the server that reads each provider's local credentials/telemetry and reports subscription rate-limit windows over a new usageLimits contract:

  • Claude: limit tracking from Claude Code's local state, plus the service, contract, and Limits UI foundation.
  • Codex: window reporting, including auth handling and fractional rate-limit values.
  • Accounts: providers are distinguished by account email, so multiple logins don't collapse into one card.
  • Grok: OIDC billing windows, including omitted-zero usage handling.
  • OpenCode: Zen subscription rate windows.

Each provider is a separate commit and reviewable on its own. Parsers are defensive and covered by focused tests (215 passing across apps/server/src/usage/). Providers without support render as placeholders, so the view degrades cleanly.

Note

Draft: before/after screenshots and a short recording of the Limits view are pending; will attach before marking ready.

Built by Claude (Fable 5) via Claude Code.

Note

Add subscription limit reporting for Claude, Codex, Grok, and OpenCode

  • Introduces UsageLimitsService and dedicated parser modules to read auth state and rate-limit windows from Claude, Codex, Grok, and OpenCode CLI tools.
  • Adds the server.getUsageLimits WebSocket RPC and a useUsageLimits React hook to fetch and aggregate provider limits across environments.
  • Adds a "Limits" view toggle to the Usage page, rendering per-provider utilization bars, reset countdowns, and availability states.
  • Risk: UsageLimitsService spawns external processes (security keychain tool on macOS, codex app-server) with bounded scopes and timeouts; ensure process cleanup handles edge cases without leaking.
📊 Macroscope summarized c92b495. 17 files reviewed, 9 issues evaluated, 0 issues filtered, 9 comments posted

🗂️ Filtered Issues

- Expose usage-limits RPC with defensive Claude OAuth parsing
- Add Usage/Limits dashboard with refresh and reset countdowns
- Document subscription limit behavior
- Read Codex rate limits through its app server
- Add auth parsing, plan labels, window mapping, and tests
- Read account authentication state alongside usage windows
- Parse raw rate-limit responses and prioritize account-wide buckets
- Fetch and expose Claude and Codex account emails
- Group and label usage cards by account identity
- Extend usage contracts and parser tests
- Read Grok OIDC billing windows and display them in Limits
- Add defensive parsers, tests, contracts, and user documentation
- Treat missing proto3 usage percentages as zero for valid credits documents
- Add regression coverage for zero-usage billing windows
- Parse OpenCode credentials and Zen rate windows
- Show OpenCode limits across contracts, UI, and docs
- Add defensive parser tests
- Replace the usage breadcrumb with an accessible heading and inline date range
@coderabbitai

coderabbitai Bot commented Aug 28, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 5f40ac06-1d61-494a-9198-53139d3695fa

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XXL 1,000+ changed lines (additions + deletions). labels Aug 28, 2026

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One convention issue found: the new UsageLimitsService reads process.env directly instead of acquiring the HostProcessEnvironment reference from the Effect environment, hiding an environment dependency in a module global. Everything else (module order, inline Context.Service interface, make/layer/layerTest exports, dependency acquisition via yield*, namespace imports, in-band failure modelling) matches the repository's Effect service conventions.

Posted via Macroscope — Effect Service Conventions

Comment thread apps/server/src/usage/UsageLimitsService.ts Outdated
Comment thread apps/server/src/usage/UsageLimitsService.ts Outdated

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two consistency findings in the new Limits view. Both are small, local fixes.

Posted via Macroscope — UI Consistency

Comment thread apps/web/src/components/usage/usageProviders.ts Outdated
Comment thread apps/web/src/components/usage/UsageLimitsContent.tsx Outdated
Comment thread apps/server/src/usage/usageLimitsCodex.ts Outdated
Comment thread docs/user/usage.md Outdated
Comment thread apps/server/src/usage/UsageLimitsService.ts Outdated
Comment thread apps/server/src/usage/UsageLimitsService.ts Outdated
Comment thread apps/server/src/usage/UsageLimitsService.ts Outdated
}
}

return GROK_DEFAULT_PROXY_BASE_URL;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 High usage/usageLimitsGrok.ts:107

When a user configures a team proxy only through [endpoints].cli_chat_proxy_base_url and models_cache.json is absent or stale, resolveGrokProxyBaseUrl returns GROK_DEFAULT_PROXY_BASE_URL, so the caller sends the team-scoped bearer to the public endpoint. The resolver never reads or receives the CLI config value; include that endpoint in resolution and return null when it cannot be parsed instead of falling back to the default.

🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/usage/usageLimitsGrok.ts around line 107:

When a user configures a team proxy only through `[endpoints].cli_chat_proxy_base_url` and `models_cache.json` is absent or stale, `resolveGrokProxyBaseUrl` returns `GROK_DEFAULT_PROXY_BASE_URL`, so the caller sends the team-scoped bearer to the public endpoint. The resolver never reads or receives the CLI config value; include that endpoint in resolution and return `null` when it cannot be parsed instead of falling back to the default.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Partially addressed in 876c8e2: a cached origin that is present but unparseable now fails closed instead of falling back to the public default. The config-file-only case (proxy set via [endpoints] cli_chat_proxy_base_url with no models cache yet) is deliberately left on the default: resolving it would mean re-parsing the CLI's TOML config (new dependency, duplicated CLI internals) to cover a transient state that ends the moment the CLI writes its models cache — and the bearer is an xAI credential reaching xAI's own default endpoint, not a third party. The resolver's doc comment now states this limitation explicitly.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I'm unable to act on this request because you do not have permissions within this repository.

Comment thread apps/server/src/usage/usageLimitsGrok.ts
Comment thread apps/server/src/usage/usageLimitsOpenCode.ts Outdated
Comment thread apps/server/src/usage/usageLimitsClaude.ts
…ides

- Read env through HostProcessEnvironment like the sibling UsageService
- Expand and resolve GROK_HOME/GROK_AUTH_PATH overrides before use
- Honor ambient CODEX_HOME in the auth pre-check to match the app server
- Report a settings-read failure as unavailable instead of unsupported
- Fail closed on a malformed cached Grok proxy origin
- Survive overflowing Codex reset instants, invalid Claude currency codes,
  array-shaped OpenCode usage, and empty Grok period objects
- Use the Button primitive for the refresh action like the usage header
- Track the runtime contrast adjustment for the OpenCode series color
Comment on lines +574 to +575
const state = parseOpenCodeAuthState(injected);
if (state !== null) return state;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium usage/UsageLimitsService.ts:574

When OPENCODE_AUTH_CONTENT is a valid document with no recognized credential, readOpenCodeAuthState falls through to the on-disk auth.json and may report/use a stale Zen key that OpenCode is not using. Return the parsed injected state directly, including null, so a successfully parsed injected document suppresses disk auth.

-    if (state !== null) return state;
+    return state;
🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/usage/UsageLimitsService.ts around lines 574-575:

When `OPENCODE_AUTH_CONTENT` is a valid document with no recognized credential, `readOpenCodeAuthState` falls through to the on-disk `auth.json` and may report/use a stale Zen key that OpenCode is not using. Return the parsed injected state directly, including `null`, so a successfully parsed injected document suppresses disk auth.

@t3dotgg

t3dotgg commented Aug 28, 2026

Copy link
Copy Markdown
Member

Note

🤖 GPT-5.6 Sol responding on behalf of Theo

We're closing this PR as we clean up the T3 Code backlog. Thank you for taking the time to put this together.

We are keeping #1732 as the provider-limit source, #8445 as its separate composer follow-up, and #8456 for OpenCode reporting. This branch is an alternate 22-file service, and keeping it would split parser, cache, and contract ownership.

If you believe we closed this in error, please reopen the PR and leave a comment explaining what we missed.

@t3dotgg t3dotgg closed this Aug 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL 1,000+ changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants